package br.usjt.component.login.model;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

@MappedSuperclass
public abstract class AbstractEntity<T> implements Serializable {

    private static final long serialVersionUID = -1710664168031716817L;

    @Basic
    @Column(name = "DT_LAST_CHANGE")
    @Version
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastChange;

    @Column(name = "DT_CRIATION")
    @Temporal(TemporalType.TIMESTAMP)
    private Date dateCriation;

    @Override
    public String toString() {
        StringBuilder s = new StringBuilder();
        try {
            BeanInfo info = Introspector.getBeanInfo(this.getClass());
            PropertyDescriptor[] properties = info.getPropertyDescriptors();
            s.append(this.getClass() + " [");
            for (int i = 0; i < properties.length; i++) {
                if (!"class".equalsIgnoreCase(properties[i].getName())) {
                    s.append(properties[i].getName());
                    Method get = properties[i].getReadMethod();
                    if (get != null) {
                        Object innerObject = get.invoke(this);
                        if (innerObject != null) {
                            String innerObjectClassName = innerObject.getClass().getName();
                            String[] arrClassName = this.getClass().getName().split(this.getClass().getSimpleName());
                            if (innerObjectClassName.contains(arrClassName[0])) {
                                s.append(": " + discoverPKGet(innerObject).invoke(innerObject));
                            } else {
                                s.append(": " + innerObject);
                            }
                        } else {
                            s.append(": null");
                        }
                    }
                    if ((i + 1) < properties.length) {
                        s.append(" | ");
                    }
                }
            }
            s.append("]");
        } catch (Exception e) {
            return super.toString();
        }

        return s.toString();
    }

    private Method discoverPKGet(Object e) throws IntrospectionException {
        Class<?> cl = e.getClass();
        Field pkf = null;
        do {
            Field[] fs = cl.getDeclaredFields();
            for (Field f : fs) {
                if (f.isAnnotationPresent(Id.class)) {
                    pkf = f;
                    break;
                }
            }
            if (pkf == null) {
                cl = cl.getSuperclass();
                if (cl.equals(Object.class)) {
                    break;
                }
            }
        } while (pkf == null);
        Method get = null;
        PropertyDescriptor[] pds = Introspector.getBeanInfo(cl).getPropertyDescriptors();
        for (PropertyDescriptor pd : pds) {
            if (pd.getPropertyType().equals(pkf.getType())) {
                get = pd.getReadMethod();
                break;
            }
        }
        return get;
    }

    protected Date getLastChange() {
        return lastChange;
    }

    protected void setLastChange(Date lastChange) {
        this.lastChange = lastChange;
    }

    protected Date getDateCriation() {
        return dateCriation;
    }

    protected void setDateCriation(Date dateCriation) {
        this.dateCriation = dateCriation;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((dateCriation == null) ? 0 : dateCriation.hashCode());
        result = prime * result + ((lastChange == null) ? 0 : lastChange.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof AbstractEntity)) {
            return false;
        }
        AbstractEntity<?> other = (AbstractEntity<?>) obj;
        if (dateCriation == null) {
            if (other.dateCriation != null) {
                return false;
            }
        } else if (!dateCriation.equals(other.dateCriation)) {
            return false;
        }
        if (lastChange == null) {
            if (other.lastChange != null) {
                return false;
            }
        } else if (!lastChange.equals(other.lastChange)) {
            return false;
        }
        return true;
    }

}
